package com.divillysausages.dsair.windows 
{
	import com.divillysausages.dsair.DSAir;
	import flash.display.NativeWindow;
	import flash.display.NativeWindowInitOptions;
	import flash.display.NativeWindowSystemChrome;
	import flash.display.NativeWindowType;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	
	/**
	 * The window manager controls any windows that the application owns
	 * @author Damian Connolly
	 */
	public class WindowManager 
	{
		
		/********************************************************************/
		
		private static var m_instance:WindowManager	= null;	// the only instance of the window manager
		private static var m_creating:Boolean		= false;// are we creating the window manager?
		
		/********************************************************************/
		
		/**
		 * The singleton instance for the window manager
		 */
		public static function get instance():WindowManager
		{
			if ( WindowManager.m_instance == null )
			{
				WindowManager.m_creating = true;
				WindowManager.m_instance = new WindowManager;
				WindowManager.m_creating = false;
			}
			return WindowManager.m_instance;
		}
		
		/********************************************************************/
		
		private const M_MARGIN:Number = 40.0; // when moving the relative position, this is the margin increase everytime
		
		/********************************************************************/
		
		private var m_root:NativeWindow				= null; // the root native window
		private var m_windows:Vector.<NativeWindow> = null; // the windows that we own
		private var m_windowRelPos:Point			= null; // the relative window position (for when we don't specify one)
		private var m_numUntitled:int				= 0;	// the number of untitled windows that we've created (for setting the title)
		
		/********************************************************************/
		
		/**
		 * The number of windows that we currently have open
		 */
		public function get numWindows():int { return ( this.m_windows != null ) ? this.m_windows.length : 0; }
		
		/********************************************************************/
		
		/**
		 * Creates a WindowManager. As WindowManager is a singleton, this shouldn't be called
		 * directly, but rather the static instance getter instead
		 */
		public function WindowManager() 
		{
			if ( !WindowManager.m_creating )
				throw new Error( "WindowManager is a singleton. Use the static instance getter instead" );
				
			// get the root window
			this.m_root = DSAir.stage.nativeWindow;
			
			// create our relative position
			this.m_windowRelPos = new Point( this.M_MARGIN, this.M_MARGIN );
			
			// add the close event for the main window
			this.m_root.addEventListener( Event.CLOSE, this._onMainWindowClose );
		}
		
		/**
		 * Creates a window
		 * @param options Any NativeWindowInitOptions to pass along. If null, the default options will be used
		 * @param title The title for the new window, if any
		 * @param x The x position of the new window
		 * @param y The y position of the new window
		 * @param width The width of the new window
		 * @param height The height of the new window
		 * @param show Should we immediately show the window?
		 * @param giveFocus Should we just show this new window or give it focus
		 */
		public function createWindow( options:NativeWindowInitOptions = null, title:String = null, x:Number = -1.0, y:Number = -1.0, width:Number = 0.0, height:Number = 0.0, show:Boolean = true, giveFocus:Boolean = true ):NativeWindow
		{
			// if we're not supported, do nothing
			if ( !NativeWindow.isSupported )
			{
				DSAir.error( this, "Can't create new window '" + title + "' as windows aren't supported in this profile" );
				return null;
			}
			
			// create our options if needed
			if ( options == null )
			{
				options 				= new NativeWindowInitOptions;
				options.systemChrome	= NativeWindowSystemChrome.STANDARD;
				options.type			= NativeWindowType.NORMAL;
			}
			
			// create our title if it's not set
			if ( title == null )
			{
				this.m_numUntitled++;
				title = "Untitled " + this.m_numUntitled;
			}
			
			// create our width and height if it's not set
			width 	||= 240.0;
			height 	||= 180.0;
			
			// create our position if it's not set
			var autoSetPos:Boolean = false;
			if ( x == -1.0 && y == -1.0 )
			{
				autoSetPos = true;
				
				// position and update the relative position
				x = this.m_root.x + this.m_windowRelPos.x;
				y = this.m_root.y + this.m_windowRelPos.y;
				this.m_windowRelPos.x += this.M_MARGIN;
				this.m_windowRelPos.y += this.M_MARGIN;
			}
			
			// create our window
			var window:NativeWindow 	= new NativeWindow( options );
			window.stage.stageWidth		= width;
			window.stage.stageHeight	= height;
			window.x					= x;
			window.y					= y;
			window.stage.scaleMode		= StageScaleMode.NO_SCALE;
			window.stage.align			= StageAlign.TOP_LEFT;
			window.title				= title;
			window.visible				= show;
			if ( giveFocus )
				window.activate();
			
			// make sure it's on screen
			if ( window.x + window.width > DSAir.mainScreenSize.x )
			{
				window.x = DSAir.mainScreenSize.x - window.width;
				if ( autoSetPos )
					this.m_windowRelPos.x = this.m_windowRelPos.y = this.M_MARGIN; // reset the relative
			}
			if ( window.y + window.height > DSAir.mainScreenSize.y )
			{
				window.y = DSAir.mainScreenSize.y - window.height;
				if ( autoSetPos )
					this.m_windowRelPos.x = this.m_windowRelPos.y = this.M_MARGIN; // reset the relative position
			}
			
			// store the window in our vector
			if ( this.m_windows == null )
				this.m_windows = new Vector.<NativeWindow>;
			this.m_windows.push( window );
			
			// add our close event, so we can keep track of what we have
			window.addEventListener( Event.CLOSE, this._onWindowClose );
			
			// return it
			return window;
		}
		
		/**
		 * Returns a window by it's title
		 * @param title The title of the window
		 * @return The window, or null if we don't have it
		 */
		public function getWindowByTitle( title:String ):NativeWindow
		{
			// no windows, couldn't find it
			if ( this.m_windows == null )
				return null;
				
			// go through and return the right title
			for each( var window:NativeWindow in this.m_windows )
			{
				if ( window.title == title )
					return window;
			}
			
			// couldn't find it
			return null;
		}
		
		/**
		 * Centers a window on the main screen
		 * @param window The window to center
		 */
		public function centerWindowOnScreen( window:NativeWindow ):void
		{
			var bounds:Point 	= DSAir.mainScreenSize;
			window.x 			= ( bounds.x * 0.5 ) - ( window.width * 0.5 );
			window.y			= ( bounds.y * 0.5 ) - ( window.height * 0.5 );
		}
		
		/**
		 * Centers a window on the app
		 * @param window The window to center
		 */
		public function centerWindowOnApp( window:NativeWindow ):void
		{
			var bounds:Rectangle = this.m_root.bounds;
			window.x				= ( bounds.x + bounds.width * 0.5 ) - ( window.width * 0.5 );
			window.y				= ( bounds.y + bounds.height * 0.5 ) - ( window.height * 0.5 );
		}
		
		/**
		 * Gives the root window focus
		 */
		public function giveRootWindowFocus():void
		{
			this.m_root.activate();
		}
		
		/********************************************************************/
		
		// called when the main window closes - close all the other windows
		private function _onMainWindowClose( e:Event ):void
		{
			// when the main window closes, close all the other windows. Don't remove
			// the event listener on the main window as perhaps we've set the app up
			// to continue working after all the windows are closed. If that's the case
			// we need the state to still be good. If the app closes, everything's cleaned
			// up anyway
			
			// close all the other windows
			var len:int = this.m_windows.length;
			for ( var i:int = len - 1; i >= 0; i-- )
			{
				// remove the close listener (so _onWindowClose() doesn't fire)
				this.m_windows[i].removeEventListener( Event.CLOSE, this._onWindowClose );
				
				// close and null
				this.m_windows[i].close();
				this.m_windows[i] = null;
			}
			
			// clear our vec
			this.m_windows.length = 0;
		}
		
		// called when a window is closed
		private function _onWindowClose( e:Event ):void
		{
			// get the window
			var window:NativeWindow = e.target as NativeWindow;
			
			// first remove the event listener
			window.removeEventListener( Event.CLOSE, this._onWindowClose );
			
			// find the index in our vector
			var index:int = this.m_windows.indexOf( window );
			if ( index == -1 )
			{
				DSAir.error( this, "A window close but we weren't holding onto it" );
				return;
			}
			
			// remove it
			this.m_windows.splice( index, 1 );
		}
		
	}

}